﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DELVE_GAME
{
    enum PLAYER_DEFAULTS
    {
        MAX_POTENCY = 680,
        STARTING_POTENCY = 128,
        TILE_POTENCY_GAIN = 1,
    }

    public class Player
    {
        bool futureBool = false;
        int playerIndex;



        int currentPotency;
        public int futurePotencyGain;
        int currentPoints;
        bool placedBase;
        List<Virus> myViruses;
        public List<Virus> alertViruses;

        /*
         * game stats 
         */
        bool winner;
        int lifeSpan;


        HashSet<Tile> visible;
        List<Player> alliances;
        List<Player> sentAllianceRequests;
        List<Player> receivedAllianceRequests;

        public Player(Player currentPlayer) {
            futureBool = true;
            this.playerIndex = currentPlayer.playerIndex;
            this.currentPotency = currentPlayer.currentPotency;
            this.currentPoints = currentPlayer.currentPoints;
            this.placedBase = currentPlayer.placedBase;

            this.myViruses = new List<Virus>();
            foreach (Virus v in currentPlayer.myViruses) {
                this.myViruses.Add(new Virus(v));
            }

            this.visible = new HashSet<Tile>();
            /*foreach (Tile t in currentPlayer.visible) {
                this.visible.Add(t);
            }*/

            this.alliances = new List<Player>();
            /*foreach (Player p in currentPlayer.alliances) {
                this.alliances.Add(p); //alliances are the same old players, can't manipulate alliances in future so no problem
            }*/

            /*this.sentAllianceRequests = new List<Player>();
            foreach (Player p in currentPlayer.sentAllianceRequests) {
                this.sentAllianceRequests.Add(new Player(p));
            }*/ //don't need sentAlliances in the future

            /*this.receivedAllianceRequests = new List<Player>();
            foreach (Player p in currentPlayer.receivedAllianceRequests) { 
                this.receivedAllianceRequests.Add(new Player(p));
            }*/ //don't need recieveAllianceRequests in the future
        }

        public Player(int playerIndex) { 
            this.playerIndex = playerIndex;
            placedBase = false;
            this.currentPotency = (int)PLAYER_DEFAULTS.STARTING_POTENCY;
            myViruses = new List<Virus>();

            winner = false;
            lifeSpan = 0;

            visible = new HashSet<Tile>();
            alliances = new List<Player>();
            sentAllianceRequests = new List<Player>();
            receivedAllianceRequests = new List<Player>();
        }

        #region getters and setters

        public List<Virus> getMyViruses() { return myViruses; }
        //public List<Virus> getMyAlertViruses() { return alertViruses; }
        public int getPlayerIndex() {return playerIndex;}
        public int getPotency() { return currentPotency; }
        public void setPotency(int potency) { currentPotency = potency; }
        public void offsetPotency(int offset)
        {
            int offsetPotency = currentPotency + offset;
            if (offsetPotency >= 0 && offsetPotency <= (int)PLAYER_DEFAULTS.MAX_POTENCY)
            {
                currentPotency = offsetPotency;
            }
        }

        public int getPoints() { return currentPoints; }
        public void addPoints(int points){currentPoints += points;}

        public int getNumViruses() { return myViruses.Count; }
        public bool isAlive() { return (myViruses.Count > 0) || currentPotency >= (int)Virus.GET_BASE_COST(); }
        public bool hasPlacedBase() { return placedBase; }

        public bool isVisible(Tile tile) { return visible.Contains(tile); }
        public int getNumAllies() { return alliances.Count; }
        public bool isAlly(Player p) { return alliances.Contains(p); }
        public bool isTentativeAlly(Player p)
        { 
            return sentAllianceRequests.Contains(p) || receivedAllianceRequests.Contains(p); 
        }
        public int getNumTentativeAlliances() { 
            return sentAllianceRequests.Count + receivedAllianceRequests.Count; 
        }

        /*
        * game stats 
        */
        public bool isWinner() { return winner; }
        public void wonGame(int lifeSpan) 
        {
            this.lifeSpan = lifeSpan;
            winner = true; 
        }
        public int getLifeSpan() { return lifeSpan; }

        #endregion

        #region automated game logic


        //Clean up dead viruses and age living ones
        public void ageViruses()
        {
            int i = 0;
            while (i < myViruses.Count)
            {
                Virus v = myViruses[i];
                if (v.getTile() == null || !v.age())
                {
                    myViruses.RemoveAt(i);
                }
                else i++;
            }
        }

        public bool startTurn() {
            if (!placedBase && myViruses.Count > 0) placedBase = true;
            return (myViruses.Count > 0);
        }

        public void collectVisibleTiles() {
            visible.Clear();
            foreach (Virus v in myViruses)
            {
                visible.UnionWith(v.getVisibleTiles());
            }
        }

        public List<Virus> expand()
        {
            List<Virus> newViruses = new List<Virus>();

            foreach (Virus v in myViruses)
            {
                if (v.isAlive())
                {
                    List<Virus> tempViruses = new List<Virus>();
                    tempViruses = v.expand();
                    if (tempViruses != null) newViruses.AddRange(tempViruses);
                }
            }
            return newViruses;
        }

        public void addVirus(Virus v)
        {
            if (v.isAlive())
            {
                offsetPotency(1);
                v.activate();
                myViruses.Add(v);
            }
        }

        //Activate viruses to let them defend against enemies
        public void endTurn()
        {
            foreach (Virus v in myViruses)
            {
                List<int> virusFriends = new List<int>();
                foreach(Player p in alliances) {
                    virusFriends.Add(p.getPlayerIndex());
                }
                //v.tryExpand(v);
                v.updateAlliances(virusFriends);
                visible.UnionWith(v.getVisibleTiles());
            }

            if (receivedAllianceRequests != null)
            {
                while (receivedAllianceRequests.Count > 0)
                {
                    respondToRequest(receivedAllianceRequests[0], false);
                }
            }
        }

        public void die(int lifeSpan)
        {
            this.lifeSpan = lifeSpan;

            while(alliances.Count > 0)
                breakAlliance(alliances[0]);

            while (receivedAllianceRequests.Count > 0)
                respondToRequest(receivedAllianceRequests[0], false);

            while (sentAllianceRequests.Count > 0)
            {
                sentAllianceRequests[0].respondToRequest(receivedAllianceRequests[0], false);
                sentAllianceRequests.RemoveAt(0);
            }
        }

        public override bool Equals(Object obj)
        {
            if (this != null & obj != null & this.getPlayerIndex() == (((Player)obj).getPlayerIndex()))
                return true;
            else
                return false;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
  

        #endregion

        #region user-controlled

        public bool placeBase(Tile tile)
        {
            if (currentPotency >= Virus.GET_BASE_COST())
            {
                Virus newVirus = tile.placeBase(this.playerIndex);
                if (newVirus != null)
                {
                    this.myViruses.Add(newVirus);
                    offsetPotency(-1 * Virus.GET_BASE_COST());
                    visible.UnionWith(newVirus.getVisibleTiles());
                    return true;
                }
            }
            return false;
        }

        public List<int> displayUpgrades(Tile tile)
        {
            List<int> upgrades = null;
            if (tile.isOccupied() && Virus.isFriend(playerIndex, tile.getVirus())) 
            {
                upgrades = tile.getVirus().displayUpgrades(currentPotency);
            }
            return upgrades;
        }

        public List<int> displayUpgrades(List<Tile> tiles)
        {
            List<int> upgradeCosts = new List<int>();
            for (int i = 0; i < (int)VIRUS_DEFAULTS.NUM_UPGRADES; i++)
                upgradeCosts.Add(0);
            foreach (Tile t in tiles)
            {
                if (t.isOccupied() && t.getVirus().getPlayerIndex() == playerIndex)
                {
                    Virus v = t.getVirus();
                    foreach (int i in v.displayUpgrades(currentPotency))
                    {
                        upgradeCosts[i] += v.upgradeCost(i);
                    }
                }
            }
            //Determine which upgrades you can afford
            List<int> availableUpgrades = new List<int>();
            for(int i = 0; i < upgradeCosts.Count; i++)
            {
                if (upgradeCosts[i] <= currentPotency && upgradeCosts[i] != 0)
                    availableUpgrades.Add(i);
            }
            if (availableUpgrades.Count == 0) availableUpgrades = null;
            return availableUpgrades;
        }

        public void applyUpgrade(Virus v, int upgrade)
        {
            int potencyOffset = 0;
            potencyOffset = -1*v.addUpgrade(upgrade);
            offsetPotency(potencyOffset);
        }

        #endregion

        #region alliances

        //Sender side
        public bool sendAllianceRequest(Player p, int currentNumPlayers)
        {
            bool sent = false;
            int alliancesAndTents = alliances.Count + getNumTentativeAlliances();
            int otherAlliancesAndTents = p.getNumAllies() + p.getNumTentativeAlliances();
            if (!isAlly(p) && !isTentativeAlly(p) && 
                alliancesAndTents < currentNumPlayers - 2 && otherAlliancesAndTents < currentNumPlayers - 2)
            {
                sentAllianceRequests.Add(p);
                p.receiveAllianceRequest(this);
                sent = true;
            }
            return sent;
        }
        public void requestResponseReceived(Player p, bool accepted)
        {
            sentAllianceRequests.Remove(p);
            if(accepted) alliances.Add(p);
        }

        //Receiver side
        public void receiveAllianceRequest(Player p)
        {
            receivedAllianceRequests.Add(p);
        }
        public void respondToRequest(Player p, bool accept)
        {
            if (accept) alliances.Add(p);
            receivedAllianceRequests.Remove(p);
            p.requestResponseReceived(this, accept);
        }

        //Both sides
        public void breakAlliance(Player p)
        {
            if(alliances.Contains(p)) {
                alliances.Remove(p);
                p.breakAlliance(this);
            }
        }

        public void resetAlliances()
        {
            alliances.Clear();
            sentAllianceRequests.Clear();
            receivedAllianceRequests.Clear();
        }


        //***********************************************************************Rick Alert Changes*************************************************************************
        //Activate viruses to let them defend against enemies
        /*
        public void endTurn()
        {
            foreach (Virus v in myViruses)
            {
                v.activate();
                visible.UnionWith(v.getVisibleTiles());
                v.tryExpand(v);
                if(!v.getTile().surroundedByAlly(v))
                {
                    v.tryExpand(v);
                }
            }
        }
        */
        //***********************************************************************Rick Alert Changes*************************************************************************


        #endregion

    }
}